home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 2002 November / SGI Freeware 2002 November - Disc 3.iso / dist / fw_qt3.idb / usr / freeware / Qt / examples / tictac / tictac.cpp.z / tictac.cpp
C/C++ Source or Header  |  2002-04-08  |  11KB  |  377 lines

  1. /****************************************************************************
  2. ** $Id:  qt/tictac.cpp   3.0.3   edited Nov 14 18:02 $
  3. **
  4. ** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
  5. **
  6. ** This file is part of an example program for Qt.  This example
  7. ** program may be used, distributed and modified without limitation.
  8. **
  9. *****************************************************************************/
  10.  
  11. #include "tictac.h"
  12. #include <qapplication.h>
  13. #include <qpainter.h>
  14. #include <qdrawutil.h>
  15. #include <qcombobox.h>
  16. #include <qcheckbox.h>
  17. #include <qlabel.h>
  18. #include <qlayout.h>
  19. #include <stdlib.h>                // rand() function
  20. #include <qdatetime.h>                // seed for rand()
  21.  
  22.  
  23. //***************************************************************************
  24. //* TicTacButton member functions
  25. //***************************************************************************
  26.  
  27. // --------------------------------------------------------------------------
  28. // Creates a TicTacButton
  29. //
  30.  
  31. TicTacButton::TicTacButton( QWidget *parent ) : QPushButton( parent )
  32. {
  33.     t = Blank;                    // initial type
  34. }
  35.  
  36. // --------------------------------------------------------------------------
  37. // Paints TicTacButton
  38. //
  39.  
  40. void TicTacButton::drawButtonLabel( QPainter *p )
  41. {
  42.     QRect r = rect();
  43.     p->setPen( QPen( white,2 ) );        // set fat pen
  44.     if ( t == Circle ) {
  45.     p->drawEllipse( r.left()+4, r.top()+4, r.width()-8, r.height()-8 );
  46.     } else if ( t == Cross ) {            // draw cross
  47.     p->drawLine( r.topLeft()   +QPoint(4,4), r.bottomRight()-QPoint(4,4));
  48.     p->drawLine( r.bottomLeft()+QPoint(4,-4),r.topRight()   -QPoint(4,-4));
  49.     }
  50. }
  51.  
  52.  
  53. //***************************************************************************
  54. //* TicTacGameBoard member functions
  55. //***************************************************************************
  56.  
  57. // --------------------------------------------------------------------------
  58. // Creates a game board with N x N buttons and connects the "clicked()"
  59. // signal of all buttons to the "buttonClicked()" slot.
  60. //
  61.  
  62. TicTacGameBoard::TicTacGameBoard( int n, QWidget *parent, const char *name )
  63.     : QWidget( parent, name )
  64. {
  65.     st = Init;                    // initial state
  66.     nBoard = n;
  67.     n *= n;                    // make square
  68.     comp_starts = FALSE;            // human starts
  69.     buttons = new TicTacButtons(n);        // create real buttons
  70.     btArray = new TicTacArray(n);        // create button model
  71.     QGridLayout * grid = new QGridLayout( this, nBoard, nBoard, 4 );
  72.     QPalette p( blue );
  73.     for ( int i=0; i<n; i++ ) {            // create and connect buttons
  74.     TicTacButton *ttb = new TicTacButton( this );
  75.     ttb->setPalette( p );
  76.     ttb->setEnabled( FALSE );
  77.     connect( ttb, SIGNAL(clicked()), SLOT(buttonClicked()) );
  78.     grid->addWidget( ttb, i%nBoard, i/nBoard );
  79.     buttons->insert( i, ttb );
  80.     btArray->at(i) = TicTacButton::Blank;    // initial button type
  81.     }
  82.     QTime t = QTime::currentTime();        // set random seed
  83.     srand( t.hour()*12+t.minute()*60+t.second()*60 );
  84. }
  85.  
  86. TicTacGameBoard::~TicTacGameBoard()
  87. {
  88.     delete buttons;
  89.     delete btArray;
  90. }
  91.  
  92.  
  93. // --------------------------------------------------------------------------
  94. // TicTacGameBoard::computerStarts( bool v )
  95. //
  96. // Computer starts if v=TRUE. The human starts by default.
  97. //
  98.  
  99. void TicTacGameBoard::computerStarts( bool v )
  100. {
  101.     comp_starts = v;
  102. }
  103.  
  104.  
  105. // --------------------------------------------------------------------------
  106. // TicTacGameBoard::newGame()
  107. //
  108. // Clears the game board and prepares for a new game
  109. //
  110.  
  111. void TicTacGameBoard::newGame()
  112. {
  113.     st = HumansTurn;
  114.     for ( int i=0; i<nBoard*nBoard; i++ )
  115.     btArray->at(i) = TicTacButton::Blank;
  116.     if ( comp_starts )
  117.     computerMove();
  118.     else
  119.     updateButtons();
  120. }
  121.  
  122.  
  123. // --------------------------------------------------------------------------
  124. // TicTacGameBoard::buttonClicked()        - SLOT
  125. //
  126. // This slot is activated when a TicTacButton emits the signal "clicked()",
  127. // i.e. the user has clicked on a TicTacButton.
  128. //
  129.  
  130. void TicTacGameBoard::buttonClicked()
  131. {
  132.     if ( st != HumansTurn )            // not ready
  133.     return;
  134.     int i = buttons->findRef( (TicTacButton*)sender() );
  135.     TicTacButton *b = buttons->at(i);        // get piece that was pressed
  136.     if ( b->type() == TicTacButton::Blank ) {    // empty piece?
  137.     btArray->at(i) = TicTacButton::Circle;
  138.     updateButtons();
  139.     if ( checkBoard( btArray ) == 0 )    // not a winning move?
  140.         computerMove();
  141.     int s = checkBoard( btArray );
  142.     if ( s ) {                // any winners yet?
  143.         st = s == TicTacButton::Circle ? HumanWon : ComputerWon;
  144.         emit finished();
  145.     }
  146.     }
  147. }
  148.  
  149.  
  150. // --------------------------------------------------------------------------
  151. // TicTacGameBoard::updateButtons()
  152. //
  153. // Updates all buttons that have changed state
  154. //
  155.  
  156. void TicTacGameBoard::updateButtons()
  157. {
  158.     for ( int i=0; i<nBoard*nBoard; i++ ) {
  159.     if ( buttons->at(i)->type() != btArray->at(i) )
  160.         buttons->at(i)->setType( (TicTacButton::Type)btArray->at(i) );
  161.     buttons->at(i)->setEnabled( buttons->at(i)->type() ==
  162.                     TicTacButton::Blank );
  163.     }
  164. }
  165.  
  166.  
  167. // --------------------------------------------------------------------------
  168. // TicTacGameBoard::checkBoard()
  169. //
  170. // Checks if one of the players won the game, works for any board size.
  171. //
  172. // Returns:
  173. //  - TicTacButton::Cross  if the player with X buttons won
  174. //  - TicTacButton::Circle if the player with O buttons won
  175. //  - Zero (0) if there is no winner yet
  176. //
  177.  
  178. int TicTacGameBoard::checkBoard( TicTacArray *a )
  179. {
  180.     int  t = 0;
  181.     int  row, col;
  182.     bool won = FALSE;
  183.     for ( row=0; row<nBoard && !won; row++ ) {    // check horizontal
  184.     t = a->at(row*nBoard);
  185.     if ( t == TicTacButton::Blank )
  186.         continue;
  187.     col = 1;
  188.     while ( col<nBoard && a->at(row*nBoard+col) == t )
  189.         col++;
  190.     if ( col == nBoard )
  191.         won = TRUE;
  192.     }
  193.     for ( col=0; col<nBoard && !won; col++ ) {    // check vertical
  194.     t = a->at(col);
  195.     if ( t == TicTacButton::Blank )
  196.         continue;
  197.     row = 1;
  198.     while ( row<nBoard && a->at(row*nBoard+col) == t )
  199.         row++;
  200.     if ( row == nBoard )
  201.         won = TRUE;
  202.     }
  203.     if ( !won ) {                // check diagonal top left
  204.     t = a->at(0);                //   to bottom right
  205.     if ( t != TicTacButton::Blank ) {
  206.         int i = 1;
  207.         while ( i<nBoard && a->at(i*nBoard+i) == t )
  208.         i++;
  209.         if ( i == nBoard )
  210.         won = TRUE;
  211.     }
  212.     }
  213.     if ( !won ) {                // check diagonal bottom left
  214.     int j = nBoard-1;            //   to top right
  215.     int i = 0;
  216.     t = a->at(i+j*nBoard);
  217.     if ( t != TicTacButton::Blank ) {
  218.         i++; j--;
  219.         while ( i<nBoard && a->at(i+j*nBoard) == t ) {
  220.         i++; j--;
  221.         }
  222.         if ( i == nBoard )
  223.         won = TRUE;
  224.     }
  225.     }
  226.     if ( !won )                    // no winner
  227.     t = 0;
  228.     return t;
  229. }
  230.  
  231.  
  232. // --------------------------------------------------------------------------
  233. // TicTacGameBoard::computerMove()
  234. //
  235. // Puts a piece on the game board. Very, very simple.
  236. //
  237.  
  238. void TicTacGameBoard::computerMove()
  239. {
  240.     int numButtons = nBoard*nBoard;
  241.     int *altv = new int[numButtons];        // buttons alternatives
  242.     int altc = 0;
  243.     int stopHuman = -1;
  244.     TicTacArray a = btArray->copy();
  245.     int i;
  246.     for ( i=0; i<numButtons; i++ ) {        // try all positions
  247.     if ( a[i] != TicTacButton::Blank )    // already a piece there
  248.         continue;
  249.     a[i] = TicTacButton::Cross;        // test if computer wins
  250.     if ( checkBoard(&a) == a[i] ) {        // computer will win
  251.         st = ComputerWon;
  252.         stopHuman = -1;
  253.         break;
  254.     }
  255.     a[i] = TicTacButton::Circle;        // test if human wins
  256.     if ( checkBoard(&a) == a[i] ) {        // oops...
  257.         stopHuman = i;            // remember position
  258.         a[i] = TicTacButton::Blank;        // restore button
  259.         continue;                // computer still might win
  260.     }
  261.     a[i] = TicTacButton::Blank;        // restore button
  262.     altv[altc++] = i;            // remember alternative
  263.     }
  264.     if ( stopHuman >= 0 )            // must stop human from winning
  265.     a[stopHuman] = TicTacButton::Cross;
  266.     else if ( i == numButtons ) {        // tried all alternatives
  267.     if ( altc > 0 )                // set random piece
  268.         a[altv[rand()%(altc--)]] = TicTacButton::Cross;
  269.     if ( altc == 0 ) {            // no more blanks
  270.         st = NobodyWon;
  271.         emit finished();
  272.     }
  273.     }
  274.     *btArray = a;                // update model
  275.     updateButtons();                // update buttons
  276.     delete[] altv;
  277. }
  278.  
  279.  
  280. //***************************************************************************
  281. //* TicTacToe member functions
  282. //***************************************************************************
  283.  
  284. // --------------------------------------------------------------------------
  285. // Creates a game widget with a game board and two push buttons, and connects
  286. // signals of child widgets to slots.
  287. //
  288.  
  289. TicTacToe::TicTacToe( int boardSize, QWidget *parent, const char *name )
  290.     : QWidget( parent, name )
  291. {
  292.     QVBoxLayout * l = new QVBoxLayout( this, 6 );
  293.  
  294.     // Create a message label
  295.  
  296.     message = new QLabel( this );
  297.     message->setFrameStyle( QFrame::WinPanel | QFrame::Sunken );
  298.     message->setAlignment( AlignCenter );
  299.     l->addWidget( message );
  300.  
  301.     // Create the game board and connect the signal finished() to this
  302.     // gameOver() slot
  303.  
  304.     board = new TicTacGameBoard( boardSize, this );
  305.     connect( board, SIGNAL(finished()), SLOT(gameOver()) );
  306.     l->addWidget( board );
  307.  
  308.     // Create a horizontal frame line
  309.  
  310.     QFrame *line = new QFrame( this );
  311.     line->setFrameStyle( QFrame::HLine | QFrame::Sunken );
  312.     l->addWidget( line );
  313.  
  314.     // Create the combo box for deciding who should start, and
  315.     // connect its clicked() signals to the buttonClicked() slot
  316.  
  317.     whoStarts = new QComboBox( this );
  318.     whoStarts->insertItem( "Computer starts" );
  319.     whoStarts->insertItem( "Human starts" );
  320.     l->addWidget( whoStarts );
  321.  
  322.     // Create the push buttons and connect their clicked() signals
  323.     // to this right slots.
  324.  
  325.     newGame = new QPushButton( "Play!", this );
  326.     connect( newGame, SIGNAL(clicked()), SLOT(newGameClicked()) );
  327.     quit = new QPushButton( "Quit", this );
  328.     connect( quit, SIGNAL(clicked()), qApp, SLOT(quit()) );
  329.     QHBoxLayout * b = new QHBoxLayout;
  330.     l->addLayout( b );
  331.     b->addWidget( newGame );
  332.     b->addWidget( quit );
  333.  
  334.     newState();
  335. }
  336.  
  337.  
  338. // --------------------------------------------------------------------------
  339. // TicTacToe::newGameClicked()            - SLOT
  340. //
  341. // This slot is activated when the new game button is clicked.
  342. //
  343.  
  344. void TicTacToe::newGameClicked()
  345. {
  346.     board->computerStarts( whoStarts->currentItem() == 0 );
  347.     board->newGame();
  348.     newState();
  349. }
  350.  
  351.  
  352. // --------------------------------------------------------------------------
  353. // TicTacToe::gameOver()            - SLOT
  354. //
  355. // This slot is activated when the TicTacGameBoard emits the signal
  356. // "finished()", i.e. when a player has won or when it is a draw.
  357. //
  358.  
  359. void TicTacToe::gameOver()
  360. {
  361.     newState();                    // update text box
  362. }
  363.  
  364.  
  365. // --------------------------------------------------------------------------
  366. // Updates the message to reflect a new state.
  367. //
  368.  
  369. void TicTacToe::newState()
  370. {
  371.     static const char *msg[] = {        // TicTacGameBoard::State texts
  372.     "Click Play to start", "Make your move",
  373.     "You won!", "Computer won!", "It's a draw" };
  374.     message->setText( msg[board->state()] );
  375.     return;
  376. }
  377.